/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package dale2507.gates.data;

import dale2507.gates.GateDirectory;
import dale2507.gates.GatePlugin;
import dale2507.gates.gate.*;
import java.io.*;
import java.lang.reflect.Constructor;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;

/**
 *
 * @author Dale
 */
public class GateLoader {
    
    private final static int LATEST_VERSION = 10;
    
    private final static int KEY_IRIS = 2;
    
    private static GateLoader instance;
    
    private GateLoader() {
    }
    
    public static GateLoader getInstance() {
        if (instance == null) {
            instance = new GateLoader();
        }
        return instance;
    }
    
    public void load() {
        if (!GatePlugin.getInstance().getDataFolder().exists()) GatePlugin.getInstance().getDataFolder().mkdirs();
        for (File f : GatePlugin.getInstance().getDataFolder().listFiles(new GateFileFilter())) {
            FileInputStream fis = null;
            DataInputStream dis = null;
            try {
                fis = new FileInputStream(f);
                dis = new DataInputStream(fis);

                Gate g;
                int version = dis.readInt();
                switch (version) {
                    case 10:
                        String name = f.getName().substring(0, f.getName().lastIndexOf('.'));
                        loadGateFileV10(name, dis);
                        Logger.getLogger(GateLoader.class.getName()).log(Level.FINE, "Gate file V10 loaded: " + name);
                        break;
                    case 9:
                        g = loadGateFileV9(dis);
                        Logger.getLogger(GateLoader.class.getName()).log(Level.FINE, "Gate file V9 loaded: " + g.getSettings().getName());
                        break;
                    case 8:
                        g = loadGateFileV8(dis);
                        Logger.getLogger(GateLoader.class.getName()).log(Level.FINE, "Gate file V8 loaded: " + g.getSettings().getName());
                        break;
                    default:
                        Logger.getLogger(GateLoader.class.getName()).log(Level.SEVERE, "Invalid configuration file: {0}", f.getCanonicalPath());
                }
            } catch (Exception ex) {
                Logger.getLogger(GateLoader.class.getName()).log(Level.SEVERE, "Error loading gate file: " + f.getName(), ex);
            } finally {
                try {
                    dis.close();
                    fis.close();
                } catch (IOException ex) {
                    Logger.getLogger(GateLoader.class.getName()).log(Level.SEVERE, "Error closing gate file: " + f.getName(), ex);
                }
            }
        }
        
        GateDirectory.getInstance().dialIndefiniteGates();
    }
    
    public void saveGate(Gate g) {
        FileOutputStream fos = null;
        DataOutputStream dos = null;
        try {
            File dir = new File("plugins/Gates");
            if (dir.exists()) dir.mkdirs();
            
            File file = new File(dir, g.getSettings().getName() + ".mcg");
            if (file.exists()) file.createNewFile();
            
            fos = new FileOutputStream(file);
            dos = new DataOutputStream(fos);
            
            dos.writeInt(GateLoader.LATEST_VERSION);
            
            // Save Ring
            dos.writeUTF(g.getRing().getClass().getName());
            g.getRing().save(dos);
            
            // Save other componenets
            writeLocation(dos, g.getDhd().getBlock().getLocation());
            writeLocation(dos, g.getDisplay().getBlock().getLocation());
            
            // Save settings
            for (int x=0; x<9; x++) {
                AddressItem item = null;
                if (g.getSettings().getAddress() != null) item = g.getSettings().getAddress().getAddressItem(x);
                if (item == null) {
                    dos.writeInt(0);
                } else {
                    dos.writeInt(item.getMaterial().getId());
                    dos.writeInt(item.getAmount());
                    dos.writeShort(item.getSubid());
                }
            }
            for (int x=0; x<9; x++) {
                AddressItem item = null;
                if (g.getSettings().getAutoDialAddress() != null) item = g.getSettings().getAutoDialAddress().getAddressItem(x);
                if (item == null) {
                    dos.writeInt(0);
                } else {
                    dos.writeInt(item.getMaterial().getId());
                    dos.writeInt(item.getAmount());
                    dos.writeShort(item.getSubid());
                }
            }
            if (g.getSettings().getIrisKey() != null) {
                dos.writeInt(g.getSettings().getIrisKey().getId());
            } else {
                dos.writeInt(0);
            }
            if (g.getSettings().getTorchType() == null) {
                dos.writeInt(0);
            } else {
                dos.writeInt(g.getSettings().getTorchType().getId());
            }
            dos.writeUTF(g.getSettings().getOwner());
            dos.writeInt(g.getSettings().getVisibility());
            dos.writeBoolean(g.getSettings().isIndefinite());
            
            Logger.getLogger(GateLoader.class.getName()).log(Level.FINE, "Gate saved: " + g.getSettings().getName());
        } catch (Exception ex) {
            Logger.getLogger(GateLoader.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                dos.close();
                fos.close();
            } catch (IOException ex) {
                Logger.getLogger(GateLoader.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    
    /**
     * @since 1.1a
     * @param dis
     * @return
     * @throws IOException 
     */
    private void loadGateFileV10(String name, DataInputStream dis) throws Exception {
        // Read Ring
        String ringClass = dis.readUTF();
        Constructor c = Class.forName(ringClass).getConstructor(DataInputStream.class);
        IRing ring = (IRing) c.newInstance(dis);
        
        // Read other components
        DHD dhd = new DHD(readLocation(dis).getBlock());
        Display display = new Display(readLocation(dis).getBlock());
        
        // Read setttings
        Address address = new Address();
        for (int x=0; x<9; x++) {
            int id = dis.readInt();
            if (id != 0) {
                AddressItem item = new AddressItem(Material.getMaterial(id), dis.readInt(), dis.readShort());
                address.setAddress(x, item);
            }
        }
        Address autoDialAddress = new Address();
        for (int x=0; x<9; x++) {
            int id = dis.readInt();
            if (id != 0) {
                AddressItem item = new AddressItem(Material.getMaterial(id), dis.readInt(), dis.readShort());
                autoDialAddress.setAddress(x, item);
            }
        }
        int key = dis.readInt();
        Material irisKey = null;
        if (key > 0) irisKey = Material.getMaterial(key);
        Material torchType = Material.getMaterial(dis.readInt());
        String owner = dis.readUTF();
        int visibility = dis.readInt();
        boolean indefinite = dis.readBoolean();
        
        GateSettings settings = new GateSettings(name, address, autoDialAddress, torchType, irisKey, owner, visibility, indefinite);
        
        new GateBuilder()
                .setDhd(dhd)
                .setDisplay(display)
                .setRing(ring)
                .setSettings(settings)
                .build(null);
    }
    
    /**
     * @since 1.0b.201206101815
     * @param dis
     * @return
     * @throws IOException 
     */
    private Gate loadGateFileV9(DataInputStream dis) throws Exception {
        String name = dis.readUTF();
        Address address = new Address();
        for (int x=0; x<9; x++) {
            int id = dis.readInt();
            if (id != 0) {
                AddressItem item = new AddressItem(Material.getMaterial(id), dis.readInt(), dis.readShort());
                address.setAddress(x, item);
            }
        }
        String owner = dis.readUTF();
        Location center = readLocation(dis);
        Block dhd = readLocation(dis).getBlock();
        Block sign = readLocation(dis).getBlock();
        int visibility = dis.readInt();
        int torchType = dis.readInt();
        Address autoDialAddress = new Address();
        for (int x=0; x<9; x++) {
            int id = dis.readInt();
            if (id != 0) {
                AddressItem item = new AddressItem(Material.getMaterial(id), dis.readInt(), dis.readShort());
                autoDialAddress.setAddress(x, item);
            }
        }

        Material irisKey = null;
        if (dis.readInt() == GateLoader.KEY_IRIS) {
            int key = dis.readInt();
            if (key > 0) irisKey = Material.getMaterial(key);
        }
        
        // Convert to new format
        IRing ring = new Ring(center);
        DHD dhdnew = new DHD(dhd);
        Display display = new Display(sign);
        GateSettings settings = new GateSettings(name, address, autoDialAddress, Material.getMaterial(torchType), irisKey, owner, visibility, false);
        
        Gate g = new GateBuilder()
                .setDhd(dhdnew)
                .setDisplay(display)
                .setRing(ring)
                .setSettings(settings)
                .build(null);
        
        return g;
    }
    
    private Gate loadGateFileV8(DataInputStream dis) throws Exception {
        String name = dis.readUTF();
        Address address = new Address();
        for (int x=0; x<9; x++) {
            int id = dis.readInt();
            if (id != 0) {
                address.setAddress(x, new AddressItem(Material.getMaterial(id)));
            }
        }
        String owner = dis.readUTF();
        Location center = readLocation(dis);
        Block dhd = readLocation(dis).getBlock();
        Block sign = readLocation(dis).getBlock();
        int visibility = dis.readInt();
        int torchType = dis.readInt();
        Address autoDialAddress = new Address();
        for (int x=0; x<9; x++) {
            int id = dis.readInt();
            if (id != 0) {
                AddressItem item = new AddressItem(Material.getMaterial(id));
                autoDialAddress.setAddress(x, item);
            }
        }

        Material irisKey = null;
        if (dis.readInt() == GateLoader.KEY_IRIS) {
            int key = dis.readInt();
            if (key > 0) irisKey = Material.getMaterial(key);
        }
        
        // Convert to new format
        IRing ring = new Ring(center);
        DHD dhdnew = new DHD(dhd);
        Display display = new Display(sign);
        GateSettings settings = new GateSettings(name, address, autoDialAddress, Material.getMaterial(torchType), irisKey, owner, visibility, false);
        
        Gate g = new GateBuilder()
                .setDhd(dhdnew)
                .setDisplay(display)
                .setRing(ring)
                .setSettings(settings)
                .build(null);
        
        return g;
    }
    
    public void deleteGate(Gate g) {
        File file = new File("plugins/Gates/" + g.getSettings().getName() + ".mcg");
        if (file.exists()) file.delete();
    }
    
    public static void writeLocation(DataOutputStream dos, Location l) throws IOException {
        dos.writeUTF(l.getWorld().getName());
        
        dos.writeDouble(l.getX());
        dos.writeDouble(l.getY());
        dos.writeDouble(l.getZ());
        
        dos.writeFloat(l.getYaw());
        dos.writeFloat(l.getPitch());
    }
    
    public static Location readLocation(DataInputStream dis) throws IOException {
        World world = GatePlugin.getInstance().getServer().getWorld(dis.readUTF());
        return new Location(world, dis.readDouble(), dis.readDouble(), dis.readDouble(), dis.readFloat(), dis.readFloat());
    }
    
    private class GateFileFilter implements FileFilter {

        @Override
        public boolean accept(File pathname) {
            if (pathname.getName().substring(pathname.getName().lastIndexOf(".") + 1).equalsIgnoreCase("mcg")) {
                return true;
            } else {
                return false;
            }
        }
        
    }
    
}
